package com.zxymq.amqp.sender;

import com.zxymq.amqp.constants.MQConstants;
import com.zxymq.amqp.support.RabbitMetaMessage;
import com.zxymq.amqp.support.ZxyJackson2JavaTypeMapper;
import com.zxymq.amqp.util.redis.RedisUtils;
import org.slf4j.Logger;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.util.Assert;

import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.UUID;


/**
 * RabbitMQ消息发送者工具 ，所有发送消息，直接使用次对抗的方法
 *
 * @author:
 * @date :2018/1/11
 */
public class RabbitSender {

    /**
     * 发送MQ消息
     *
     * @param rabbitMetaMessage Rabbit元信息对象，用于存储交换器、队列名、消息体
     * @return 消息ID
     * todo 根据发送的消息的类选择不同的策略进行message的转换
     * 1、基础数据类型 （已经测试完成 byte 和char 不行）
     * 2、list（多层泛型支持 已测试）
     * 3、map（valve 多层泛型支持 已测试）
     * 4、简单对象
     */
    public static String send(RabbitMetaMessage rabbitMetaMessage, RedisUtils redisTemplate,
                              RabbitTemplate rabbitTemplate, Logger logger) {
        Assert.notNull(rabbitMetaMessage.getConsumerTag(), "The consumerTag cannot be empty!");
        Assert.notNull(rabbitMetaMessage.getPayload(), "The message body cannot be empty!");
        //获取发送对象的classType
        System.out.println("发送消息的实际类型" + rabbitMetaMessage.getPayload().getClass().toString());
        final String msgId = UUID.randomUUID().toString();//这里的主键生成策略可以根据业务进行调整
        redisTemplate.addMap(MQConstants.MQ_PRODUCER_RETRY_KEY, msgId, rabbitMetaMessage);
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                MessageProperties messageProperties = message.getMessageProperties();
                messageProperties.setMessageId(msgId);
                // 设置消息持久化
                messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                //设置消费消息标识
                messageProperties.setConsumerTag(rabbitMetaMessage.getConsumerTag());
                //指定contentType类型
                messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
                //设置基本类型
                messageProperties.getHeaders().put("baseClass", rabbitMetaMessage.getPayload().getClass());
                //告诉消费端是否需要设置json 转换映射
              /*  当发送普通对象的JSON数据时，需要在消息的header中增加一个__TypeId__的属性告知消费者是哪个对象
                当发送List集合对象的JSON数据时，需要在消息的header中将__TypeId__指定为java.util.List，并且需要额外指定属性__ContentTypeId__用户告知消费者List集合中的对象类型
                当发送Map集合对象的JSON数据时，需要在消息的header中将__TypeId__指定为java.util.Map，并且需要额外指定属性__KeyTypeId__用于告知客户端Map中key的类型，
                __ContentTypeId__用于告知客户端Map中Value的类型*/
                messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_TYPE_ID);
                if (rabbitMetaMessage.getPayload() instanceof List) {
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_LIST_TYPE);
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_TYPE_ID);
                } else if (rabbitMetaMessage.getPayload() instanceof Map) {
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_TYPE_ID, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_TYPE_ID);
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_MAP_TYPE);
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_KEY_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_STRING_TYPE);
                    messageProperties.getHeaders().put(ZxyJackson2JavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME, ZxyJackson2JavaTypeMapper.DEFAULT_ZXY_TYPE_ID);
                }
                return message;
            }
        };

        try {
            rabbitTemplate.convertAndSend(rabbitMetaMessage.getExchange(), rabbitMetaMessage.getRoutingKey(),
                    rabbitMetaMessage.getPayload(), messagePostProcessor, new CorrelationData(msgId));

            logger.info("发送消息，消息ID:{}", msgId);

            return msgId;
        } catch (AmqpException e) {
            throw new RuntimeException("发送RabbitMQ消息失败！", e);
        }
    }


}
